%{
/*-------------------------------------------------------------------
* gram.y
*    openPlant SQL yacc rules/actions
* HISTORY
*    AUTHOR        DATE        MAJOR EVENT
*    coanor        Oct,2009    create the file in Emacs & Notepad++
*
* NOTES
*  CAPITALS are used to represent terminal symbols.
*  non-capital are used to represent non-terminals.
*--------------------------------------------------------------------  
*/

#include "primary_include_file.h"
#include <ctype.h>
#include <limits.h>
#include "nodes/makefuncs.h"
#include "nodes/op_list.h"
#include "nodes/value.h"
#include "nodes/parsenodes.h"
#include "parser/gramparse.h"


  /* location tracking support */
#define YYLLOC_DEFAULT(current, rhs, n) \
  do { \
    if (n) \
      (current) = (rhs)[1]; \
    else \
      (current) = (rhs)[0]; \
  } while(0)

/* The %name-prefix option below will make bison call
 * base_yylex,but we really want it to call filtered_base_yylex.
 */
#define base_yylex filtered_base_yylex 
  
extern List *parsetree;  /* final parse result */
static bool QueryIsRule = FALSE;

/* _line_00128 */
static void insertSelectOptions(SelectStmt *stmt, List *sortClause);

/* _line_00125 */
static List *check_indirection(List *indirection);

  /* some static function declaration */
  /* _line_00133 */
static Node *doNegate(Node *n, int location);

static void doNegateFloat(Value *v);
%}

%expect 0
%name-prefix = "base_yy"
%locations

%union
{
  int  ival;
  char chr;
  char *str;
  const char  *keyword;
  List  *list;
  Node  *node;
  Value  *value;  /* at present, used for 3 node types:
			  * T_Integer,T_Float,T_String.
			  */
  SortBy *sortby;
  TypeName  *typnam;
  Alias  *alias;
  RangeVar  *range;  /* used in FROM clause */
  ResTarget  *target;
  InsertStmt  *istmt;
}

 /********************************************************
  * 
  * we first define the rules, and then delare its type 
  *
  ********************************************************/
/* _line_00184 */
%type <node> stmt
             SelectStmt
             UpdateStmt
             InsertStmt
             DeleteStmt
/* _line_00214 */
%type <node> select_with_parens
             select_no_parens
             select_clause
             simple_select
 /* _line_00218 */
%type <ival> opt_nulls_order opt_asc_desc
/* _line_00251 */
%type <str> relation_name
            name
            attr_name
/* _line_00255 */
%type <list> qual_all_Op
/* _line_00258 */
%type <range> qualified_name
/* _line_00260 */
%type <str> all_Op MathOp
/* _line_00271 */
%type <list> stmtblock 
             stmtmulti 
             using_clause 
             from_list 
             name_list
             returning_clause 
             target_list
             insert_column_list 
             opt_indirection
             set_clause_list
             from_clause
             set_clause
             multiple_set_clause
             ctext_expr_list
             ctext_row
             set_target_list
             opt_distinct
             expr_list
             sort_clause
             sortby_list
             any_operator
             group_clause
%type <istmt> insert_rest
/* _line_00345 */
%type <node> where_or_current_clause 
             a_expr
             c_expr 
             indirection_el
             where_clause
             having_clause
 /* _line_00353 */
%type <node> ctext_expr
/* _line_00355 */
%type <alias> alias_clause
 /* _line_00356 */
%type <sortby> sortby
/* _line_00358 */
%type <node> table_ref
/* _line_00360 */
%type <range> relation_expr
/* _line_00361 */
%type <range> relation_expr_opt_alias
/* _line_00362 */
%type <target> target_el 
               insert_column_item
               single_set_clause
               set_target
/* _line_00384 */
%type <str> ColId
            ColLabel

 /* ordinary key words in alphabetical order */
 /* _line_00428 */
%token <keyword> 
  ALL AND AS ASC 
  
  AT ZONE

  BETWEEN BY

  DESC DELETE_P DEFAULT DISTINCT

  FALSE_P FROM

  GROUP_P

  HAVING

  INSERT INTO IS ISNULL

  JOIN

  LARGE_P LIKE LOCAL

  NOT NULL_P NOTNULL

  ON OR ORDER OPERATOR

  RETURNING

  SELECT SET

  TABLE TIME TIMESTAMP TRUE_P TEMP TEMPORARY

  UPDATE USING

  VALUES

  WHERE

 /* The grammar thinks these are keywords, but they are not in
  * the kwlist.h list and so can never be entered directly. The
  * filter in parser.c create these tokens when required.
  */
 /* _line_00519 */
%token NULLS_FIRST NULLS_LAST WITH_TIME

/* Special token types, not actually keywords - seet the "lex" file */
%token <str> IDENT FCONST SCONST BCONST XCONST Op
%token <ival> ICONST PARAM

 /* precedence: lowest to highest */
%left OR
%left AND
%right NOT
%right '='
%nonassoc '<' '>'
%nonassoc LIKE
%nonassoc BETWEEN
%left POSTFIXOP  /* dummy for postfix Op rules */

%left Op  /* multi-character ops and user-defined operators */
%left '+' '-'
%left '*' '/' '%'
%left '^'
%right UMINUS
%left '(' ')'
%left '.'

%%

 /* _line_00582 */
stmtblock: stmtmulti { parsetree = $1; }
;

/* _line_00586 */
stmtmulti:  /* here, the first left-recursion */ 
  stmtmulti ';' stmt {
    if ($3 != NULL) {
      $$ = lappend($1, $3);
	} else {
      $$ = $1;
	}
  }
| stmt {
    if ($1 != NULL) {
      $$ = list_make1($1);
	} else {
      $$ = NIL;
	}
  }
;

/* _line_00600 */
stmt:
  DeleteStmt
| InsertStmt
| SelectStmt
| UpdateStmt
| /* EMPTY */ { $$ = NULL; }
;

/* _line_04597 */
opt_asc_desc: ASC { $$ = SORTBY_ASC; }
| DESC { $$ = SORTBY_DESC; }
| /* EMPTY */ { $$ = SORTBY_DEFAULT; }
;

/* _line_04602 */
opt_nulls_order: NULLS_FIRST { $$ = SORTBY_NULLS_FIRST; }
| NULLS_LAST { $$ = SORTBY_NULLS_LAST; }
| /* EMPTY */ { $$ = SORTBY_NULLS_DEFAULT; }
;

/* _line_05055 */
any_operator:
all_Op { $$ = list_make1(makeString($1)); }
| ColId '.' any_operator { $$ = lcons(makeString($1), $3); }
;

/*****************************************
 * QUERY
 *  INSERT STATEMENTS
 ****************************************/
/* _line_06565 */
InsertStmt:
INSERT INTO qualified_name insert_rest returning_clause {
  $4->relation = $3;
  $4->returningList = $5;
  $$ = (Node *)$4;
}
;

/* _line_06574 */
insert_rest:
SelectStmt {
  $$ = makeNode(InsertStmt);
  $$->cols = NIL;
  $$->selectStmt = $1;
}
| '(' insert_column_list ')' SelectStmt {
  $$ = makeNode(InsertStmt);
  $$->cols = $2;
  $$->selectStmt = $4;
}
| DEFAULT VALUES {
  $$ = makeNode(InsertStmt);
  $$->cols = NIL;
  $$->selectStmt = NULL;
}
;

/* _line_06595 */
insert_column_list:
insert_column_item { $$ = list_make1($1); }
| insert_column_list ',' insert_column_item {
  $$ = lappend($1, $3);
}
;

/* _line_06602 */
insert_column_item:
ColId opt_indirection {
  $$ = makeNode(ResTarget);
  $$->name = $1;
  $$->indirection = check_indirection($2);
  $$->location = @1;
}

/* _line_06613 */
returning_clause:
RETURNING target_list { $$ = $2; }
| /* EMPTY */ { $$ = NIL; }
;

/*****************************************
 * QUERY:
 *  DELETE STATEMENTS
 *****************************************/
/* _line_06626 */
DeleteStmt: DELETE_P FROM relation_expr_opt_alias
using_clause where_or_current_clause returning_clause 
{
  DeleteStmt *n = makeNode(DeleteStmt);
  n->relation = $3;
  n->usingClause = $4;
  n->whereClause = $5;
  n->returningList = $6;
  $$ = (Node *)n;
}
;

/* _line_06638 */
using_clause: USING from_list { $$ = $2; }
| /* empty */  { $$ = NIL; }
;

/****************************************
 * QUERY
 *  UpdateStmt(UPDATE)
 ***************************************/
/* _line_06688 */
UpdateStmt: UPDATE relation_expr_opt_alias
  SET set_clause_list
  from_clause
  where_or_current_clause
  returning_clause {
  
}
;

/* _line_06704 */
set_clause_list:
set_clause { $$ = $1; }
| set_clause_list ',' set_clause { $$ = list_concat($1, $3); } 
;

/* _line_06709 */
set_clause:
single_set_clause { $$ = $1; }
| multiple_set_clause { $$ = $1; }
;

/* _line_06714 */
single_set_clause:
set_target '=' ctext_expr {
  $$ = $1;
  $$->val = (Node *)$3;
}
;

multiple_set_clause:
'(' set_target_list ')' '=' ctext_row {
  /*
  ListCell *col_cell;
  ListCell *val_cell;
  */

  /* ... */
}
;
/* _line_06750 */
set_target:
ColId opt_indirection {
  $$ = makeNode(ResTarget);
  $$->name = $1;
  $$->indirection = check_indirection($2);
  $$->val = NULL;  /* upper production sets this */
  $$->location = @1;
}

/* _line_06761 */
set_target_list:
set_target { $$ = list_make1($1); }
| set_target_list ',' set_target { $$ = lappend($1, $3); }
;

/*****************************************
 * QUERY
 *  SELECT STATEMENTS
 ****************************************/
/* A complete SELECT statement looks like this */
/* _line_06841 */
SelectStmt: select_no_parens %prec UMINUS
| select_with_parens %prec UMINUS
;

/* _line_06845 */
select_with_parens: '(' select_no_parens ')' { $$ = $2; }
| '(' select_with_parens ')' { $$ = $2; }
;

/* 
 * This rule parse the equivalent of the standard's 
 * <query expression>. The duplicative productions are
 * are annoying, but hard to get rid of without creating
 * shift/reduce conflicts.
 */
/* _line_06860 */
select_no_parens: simple_select { $$ = $1; }
| select_clause sort_clause {
  insertSelectOptions((SelectStmt *)$1, $2);
  $$ = $1;
}
;

/* _line_06912 */
select_clause:
simple_select { $$ = $1; }
| select_with_parens { $$ = $1; }
;

/* _line_06940 */
simple_select:
SELECT opt_distinct target_list
  from_clause where_clause 
  group_clause having_clause {
  SelectStmt *n = makeNode(SelectStmt);
  n->distinctClause = $2;
  n->targetList = $3;
  n->fromClause = $4;
  n->whereClause = $5;
  n->groupClause = $6;
  n->havingClause = $7;

  $$ = (Node *)n;
}
;

/* We use (NIL) as a placeholder to indicate that all target expressions
 * should be placed in the DISTINCT list during parsetree analysis.
 */
/* _line_07104 */
opt_distinct:
DISTINCT { $$ = list_make1(NIL); }
| DISTINCT ON '(' expr_list ')' { $$ = $4; }
| ALL { $$ = NIL; }
| /* EMPTY */ { $$ = NIL; }
;

/* _line_07116 */
sort_clause: ORDER BY sortby_list { $$ = $3; }
;

/* _line_07120 */
sortby_list: sortby { $$ = list_make1($1); }
| sortby_list ',' sortby { $$ = lappend($1, $3); }
;

/* _line_07125 */
sortby: a_expr USING qual_all_Op opt_nulls_order {
  $$ = makeNode(SortBy);
  $$->node = $1;
  $$->sortby_dir = SORTBY_USING;
  $$->sortby_nulls = $4;
  $$->useOp = $3; 
  $$->location = @3;
}
| a_expr opt_asc_desc opt_nulls_order {
  $$ = makeNode(SortBy);
  $$->node = $1;
  $$->sortby_dir = $2;
  $$->sortby_nulls = $3;
  $$->useOp = NIL;
  $$->location = -1;  /* no operator */
}
;

/* _line_07223 */
group_clause:
GROUP_P BY expr_list { $$ = $3; }
| /* EMPTY */ { $$ = NIL; }
;

/* _line_07228 */
having_clause:
HAVING a_expr { $$ = $2; }
| /* EMPTY */ { $$ = NULL; }
;

/****************************************************
 *
 * clauses common to all Optimizable Stmts:
 *   from_clause - 
 *   where_clause - 
 *
 ****************************************************/

/* _line_07297 */
from_clause:
FROM from_list { $$ = $2; }
| /* EMPTY */ { $$ = NIL; }
;

/* _line_07302 */
from_list: table_ref { $$ = list_make1($1); }
| from_list ',' table_ref { $$ = lappend($1, $3); }
;

/* 
 * table_ref is where an alias clause can be attached. Note
 * we cannot make alias_clause have an empty production because
 * that causes parse conflicts between 
 *   table_ref := '(' join_table ')' alias_clause 
 * and 
 *   join_table := '(' join_table ')'. 
 * So, we must have the redundant-looking productions here instead.
 */
/* _line_07314 */
table_ref: 
relation_expr {
  $$ = (Node *)$1;
}
| relation_expr alias_clause {
  $1->alias = $2;
  $$ = (Node *)$1;
}
| select_with_parens {
  /* The SQL spec does not permit a subselect
   * (<derived_table>) without an alias clause,
   * so we don't either. This avoid the problem
   * of needing to invent a unique refname for it.
   * That could be surmounted if there's sufficient
   * popular demand, but for now let's just implement
   * the spec and see if anyone complains. However,
   * it does seem like a good idea to emit an error
   * message that's better than "syntax error".
   */
  if (IsA($1, SelectStmt)/* && ((SelectStmt *) $1)->valuesList*/)
    /* we have not emit an error message here yet, 
     * but we should do this, or we will delete the implemention:)
     */
    ;
  else
    ;
  $$ = NULL;
}
| select_with_parens alias_clause {
  RangeSubselect *n = makeNode(RangeSubselect);
  n->subquery = $1;
  n->alias = $2;
  $$ = (Node *)n;
}
;

/* _line_07498 */
alias_clause: AS ColId '(' name_list ')' {
  $$ = makeNode (Alias);
  $$->aliasname = $2;
  $$->colnames = $4;
}
| AS ColId {
  $$ = makeNode(Alias);
  $$->aliasname = $2;
}
| ColId '(' name_list ')' {
  $$ = makeNode(Alias);
  $$->aliasname = $1;
  $$->colnames = $3;
}
| ColId {
  $$ = makeNode(Alias);
  $$->aliasname = $1;
}
;

/* _line_07548_ */
relation_expr: 
qualified_name
{
  /* default inheritance */
  $$ = $1;
  $$->inhOpt = INH_DEFAULT;
}
| qualified_name '*'
{
  /* inheritance query */
  $$ = $1;
  $$->inhOpt = INH_YES;
  $$->alias = NULL;
}
;

/* Given "UPDATE foo set set...", we have to decide without looking any
 * further ahead whether the first "set" is an alias or the UPDATE's set
 * keyword. Since "set" is allowed as a column name both interpretations.
 * are feasible. We resolve the shift/reduce conlict by giving the first
 * relation_expr_opt_alias production a higher precedence than the SET token
 * has, causing the parser to prefer to reduce, in effect assuming that the
 * SET is not an alias.
 */
/* _line_07595 */
relation_expr_opt_alias: relation_expr %prec UMINUS
{
  $$ = $1;
}
| relation_expr ColId
{
  Alias *alias = makeNode(Alias);
  alias->aliasname = $2;
  $1->alias = alias;
  $$ = $1;
}
| relation_expr AS ColId
{
  Alias *alias = makeNode(Alias);
  alias->aliasname = $3;
  $1->alias = alias;
  $$ = $1;
}
;

/* _line_07620 */
where_clause:
WHERE a_expr { $$ = $2; }
| /* EMPTY */ { $$ = NULL; }
;

/* variant for UPDATE and DELETE */
/* _line_07626 */
where_or_current_clause: WHERE a_expr { $$ = $2; }
;

/*****************************************************************
 *
 * expression grammar
 *
 *****************************************************************
 */
/* general expression.
 * this is the heart of the expression syntax 
 *
 * We have two expression types: a_expr is the unrestricted kind, and
 * b_expr is a subset that must be used in some places to avoid shift/reduce
 * conflicts.  For example, we can't do BETWEEN as "BETWEEN a_expr AND a_expr"
 * because that use of AND conflicts with AND as a boolean operator.  So,
 * b_expr is used in BETWEEN and we remove boolean keywords from b_expr.
 *
 * Note that '(' a_expr ')' is a b_expr, so an unrestricted expression can
 * always be used by surrounding it with parens.
 *
 * c_expr is all the productions that are common to a_expr and b_expr;
 * it's factored out just to eliminate redundant coding.
 */
/* _line_08190 */
a_expr: 
c_expr { $$ = $1; }
/*| a_expr '+' a_expr
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", $1, $3, @2); }
| a_expr '-' a_expr
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "-", $1, $3, @2); }
| a_expr '^' a_expr
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "^", $1, $3, @2); }
| a_expr '<' a_expr
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $3, @2); }
| a_expr '>' a_expr
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $3, @2); }
| a_expr '=' a_expr
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", $1, $3, @2); }

| a_expr '*' a_expr
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "*", $1, $3, @2); }
| a_expr '/' a_expr
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "/", $1, $3, @2); }
| a_expr '%' a_expr
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "%", $1, $3, @2); }
*/
| a_expr AND a_expr
{ $$ = (Node *) makeA_Expr(AEXPR_AND, NIL, $1, $3, @2); }
| a_expr OR a_expr
{ $$ = (Node *) makeA_Expr(AEXPR_OR, NIL, $1, $3, @2); }

| NOT a_expr
{ $$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL, $2, @1); } 
;


c_expr: {}
;

/* _line_09487 */
all_Op: Op { $$ = $1; }
| MathOp { $$ = $1; }

/* _line_09491 */
MathOp: '+' { $$ = "+"; }
| '-' { $$ = "-"; }
| '*' { $$ = "*"; }
| '/' { $$ = "/"; }
| '%' { $$ = "%"; }
| '^' { $$ = "^"; }
| '<' { $$ = "<"; }
| '>' { $$ = ">"; }
| '=' { $$ = "="; }
;


/* _line_09508 */
qual_all_Op:
all_Op { $$ = list_make1(makeString($1)); }
| OPERATOR '(' any_operator ')' { $$ = $3; }
;

/* _line_9538 */
expr_list: a_expr {
  $$ = list_make1($1);
}
| expr_list ',' a_expr {
  $$ = lappend($1, $3);
}
;

/* _line_9752 */
indirection_el:
'.' attr_name {
  $$ = (Node *)makeString($2);
}
| '.' '*' {
  $$ = (Node *)makeNode(A_Star);
}
;

/* _line_9782 */
opt_indirection: /* EMPTY */ { $$ = NIL; }
| opt_indirection indirection_el { $$ = lappend($1, $2); }

/* _line_9798 */
ctext_expr:
a_expr { $$ = (Node *)$1; }
| DEFAULT {
  SetToDefault *n = makeNode(SetToDefault);
  n->location = @1;
  $$ = (Node *)n;
}

/* _line_9808 */
ctext_expr_list:
ctext_expr { $$ = list_make1($1); }
| ctext_expr_list ',' ctext_expr { $$ = lappend($1, $3); }
;

/* _line_9818 */
ctext_row: '(' ctext_expr_list ')' { $$ = $2; }
;

/*****************************************************************
 *
 * target list for SELECT
 *
 ****************************************************************/
/* _line_09828 */
target_list:
target_el { $$ = list_make1($1); }
| target_list ',' target_el { $$ = lappend($1, $3); }
;

/* _line_09833 */
target_el: 
a_expr AS ColLabel {
  $$ = makeNode(ResTarget);
  $$->name = $3;
  $$->indirection = NIL;
  $$->val = (Node *)$1;
  $$->location = @1;
}
| a_expr {
  $$ = makeNode(ResTarget);
  $$->name = NULL;
  $$->indirection = NIL;
  $$->val = (Node *)$1;
  $$->location = @1;
}
| '*' {
  ColumnRef *n = makeNode(ColumnRef);
  n->fields = list_make1(makeNode(A_Star));
  n->location = @1;

  $$ = makeNode(ResTarget);
  $$->name = NULL;
  $$->indirection = NIL;
  $$->val = (Node *)n;
  $$->location = @1;
}
;
/*****************************************************************
 *
 * Names and constants
 *
 ****************************************************************/

/* _line_09847 */

 
 /* _line_09886 */
relation_name:
ColId { $$ = $1; }
;

/* The production for a qualified relation name has to exactly
 * match the production for a qualified func_name, because in a 
 * FROM clause we cannot tell which we are parsing until we see
 * what comes after it ('(' for a func_name, something else for
 * a relation). Therefore we allow 'indirection' which may contain
 * subscripts, and reject that case in the C code.
 */
/* _line_09903 */
qualified_name:
relation_name {
  $$ = makeNode(RangeVar);
  $$->catalogname = NULL;
  $$->schemaname = NULL;

  $$->relname = $1;
  $$->location = @1;
}
;

/* _line_09940 */
name_list: name {
  $$ = list_make1(makeString($1));
}
| name_list ',' name {
  $$ = lappend($1, makeString($3));
}
;

/* _line_09947 */
name: ColId { $$ = $1; }
;

/* _line_9955 */
attr_name: ColLabel { $$ = $1; }
;
/* Name classification hierarchy
 * 
 * IDENT is the lexme returned by the lexer for identifiers
 * that match no known keyword. In most cases, we can accept
 * certain keywords as names, not only IDENTs. We prefer to 
 * accept as many such keywords as possible to minimize the
 * impact of "reserve words" on programmers. So, we divide
 * names into serveral possible classes. The classification
 * is chosen in part to make keywords acceptable as names
 * wherever possible.
 */

/* Column identifier --- names that can be column, table, etc names. */
/* _line_10082 */
ColId: IDENT { $$ = $1; }
;

/* _line_10097 */
/* Column label -- allowed labels in "AS" clauses. */
ColLabel: IDENT { $$ = $1; }
;

%%

/* check_indirection -- check the result of indirection production
 * we only allow '*' at the end of the list, but it's hard to enforce that.
 */
	  /* _line_10815 */
static List *
check_indirection(List *indirection)
{
  ListCell *l;
  foreach(l, indirection) {
    if (IsA(lfirst(l), A_Star)) {
	  if (lnext(l) != NULL)
	    yyerror("improper use of \"*\"");
	}
  }
  
  return indirection;
}

/* insertSelectOptions()
 * Insert ORDER BY, etc into an already-constructed SelectStmt.
 * This routine is just to avoid duplicating code in SelectStmt productions.
 */
 /* _line_10869 */
static void
insertSelectOptions(SelectStmt *stmt, List *sortClause) 
{
  Assert(IsA(stmt, SelectStmt));
  
  if (sortClause) {
    if (stmt->sortClause)
	  printf("multiple ORDER BY clauses not allowed.\n");
	stmt->sortClause = sortClause;
  }  
}

/* _line_11093 */
static void
doNegateFloat(Value *v)
{
	char   *oldval = v->val.str;

	Assert(IsA(v, Float));
	if (*oldval == '+')
		oldval++;
	if (*oldval == '-')
		v->val.str = oldval+1;	/* just strip the '-' */
	else
	{
		char   *newval = (char *) malloc(strlen(oldval) + 2);

		*newval = '-';
		strcpy(newval+1, oldval);
		v->val.str = newval;
	}
}

/* parser_init()
 * Initialize to parse one query string
 */
/* _line_11048 */
void parser_init(void)
{
  QueryIsRule = FALSE;
}

/* doNegate()
 * Handle negation of a numeric constant.
 *
 * Formerly, we did this here because the optimizer couldn't cope with
 * indexquals that looked like "var = -4" --- it wants "var = const"
 * and a unary minus operator applied to a constant didn't qualify.
 */
 /*_line_28591 */
static Node *doNegate(Node *n, int location)
{
  if (IsA(n, A_Const)) {
    A_Const *con = (A_Const *)n;
    /* report the constant's location as that of the '-' sign */
    con->location = location;
    if (con->val.type == T_Integer) {
      con->val.val.ival = -con->val.val.ival;
      return n;
    }
    if (con->val.type == T_Float) {
      doNegateFloat(&con->val);
      return n;
    }
  }
  
  return (Node *)makeSimpleA_Expr(AEXPR_OP, "-", NULL, n, location);
}

#undef base_yylex

#include "lex.base_yy.c"
